import java.io.FileWriter;
import java.io.IOException;
import java.util.Date;
import java.util.Stack;
import java.util.Vector;
import java.lang.StringBuffer;


public class CodeGenerator {
	private String fileName = "rc.s";
	private String moduleName = "main";
	/* Counters */
	private int strCount = 0;
	private int floatCount = 0;
	private int printBoolCount = 0;
    private int indent_level = 0;
    private int ifStmtCount = 0;
    private int branchCount = 0;
    private int andCount = 0;
    private int orCount = 0;
    private int loopCount = 0;
    private static int staticCount = 0;
    
    private static final String ERROR_IO_CLOSE = 
        "Unable to close fileWriter";
    private static final String ERROR_IO_CONSTRUCT = 
        "Unable to construct FileWriter for file %s";
    private static final String ERROR_IO_WRITE = 
        "Unable to write to fileWriter";

    private FileWriter fileWriter;
    
    private static final String FILE_HEADER = 
        "/*\n" +
        " * Generated %s\n" + 
        " */\n\n";
        
    /***********************REGISTERS **************************/
    private static final String OUT_0 = "%o0";
    private static final String OUT_1 = "%o1";
    private static final String OUT_2 = "%o2";
    
    private static final String IN_1 = "%i1";
    
    private static final String LOCAL_0 = "%l0";
    private static final String LOCAL_1 = "%l1";
    private static final String LOCAL_2 = "%l2";
    
    private static final String GLOBAL_0 = "%g0";
    private static final String GLOBAL_1 = "%g1";
    
    private static final String FLOAT_0 = "%f0";
    private static final String FLOAT_1 = "%f1";
    private static final String FLOAT_2 = "%f2";
    
    private static final String SP = "%sp";
    private static final String FP = "%fp";
    /********************** FORMATTERS *************************/
    private static final String SEPARATOR = "\t";

    private static final String INT_FMT = "%d";
    private static final String STR_FMT = "%s";
    private static final String NL = "\n";
    private static final String HEADER_INT = "\"%d\"";
    private static final String HEADER_STRING = "\"%s\"";
    private static final String HEADER_NL = "\"\\n\"";
    private static final String HEADER_BOOL_TRUE = "\"true\"";
    private static final String HEADER_BOOL_FALSE = "\"false\"";
    
    /************************** OPERATORS **********************/
    private static final String SET_OP = "set";
    private static final String MOVE_OP = "mov";
    //private static final String NEG_OP = "neg";
    private static final String STORE_OP = SEPARATOR + "st" + SEPARATOR + SEPARATOR + "%s, [%s]" + NL;
    private static final String ADD_OP = "add" + SEPARATOR;
    private static final String SUB_OP = "sub" + SEPARATOR;
    private static final String OR_OP = "or" + SEPARATOR; // logical
    private static final String AND_OP = "and" + SEPARATOR;
    private static final String XOR_OP = "xor" + SEPARATOR;
    private static final String NOP = "nop" + NL + NL;
    private static final String CALL_OP = SEPARATOR + "call" + SEPARATOR + STR_FMT + NL + SEPARATOR + NOP;
    private static final String BLE_OP = SEPARATOR + "ble" + SEPARATOR + STR_FMT + NL + SEPARATOR + NOP;
    private static final String BGE_OP = SEPARATOR + "bge" + SEPARATOR + STR_FMT + NL + SEPARATOR + NOP;
    private static final String BL_OP = SEPARATOR + "bl" + SEPARATOR + STR_FMT + NL + SEPARATOR + NOP;
    private static final String BG_OP = SEPARATOR + "bg" + SEPARATOR + SEPARATOR + STR_FMT + NL + SEPARATOR + NOP;
    private static final String BE_OP = SEPARATOR + "be" + SEPARATOR + SEPARATOR + STR_FMT + NL + SEPARATOR + NOP;
    private static final String BNE_OP = SEPARATOR + "bne" + SEPARATOR + SEPARATOR + STR_FMT + NL + SEPARATOR + NOP;
    private static final String BA_OP = SEPARATOR + "ba" + SEPARATOR + SEPARATOR + STR_FMT + NL + SEPARATOR + NOP;
    private static final String CMP_OP = "cmp";

    
    private static final String SAVE_OP = "save";
    private static final String FITOS_OP = "fitos";
    private static final String RETURN = SEPARATOR + "ret" + NL;
    private static final String RESTORE = SEPARATOR + "restore" + NL + NL;
    // float operations
    private static final String FADDS_OP = "fadds" + SEPARATOR;
    private static final String FSUBS_OP = "fsubs" + SEPARATOR;
    private static final String FMULS_OP = "fmuls" + SEPARATOR;
    private static final String FDIVS_OP = "fdivs" + SEPARATOR;
    private static final String FCMPS_OP = "fcmps";
    private static final String FBG_OP = SEPARATOR + "fbg" + SEPARATOR + STR_FMT + NL + SEPARATOR + NOP;
    private static final String FBGE_OP = SEPARATOR + "fbge" + SEPARATOR + STR_FMT + NL + SEPARATOR + NOP;
    private static final String FBL_OP = SEPARATOR + "fbl" + SEPARATOR + STR_FMT + NL + SEPARATOR + NOP;
    private static final String FBLE_OP = SEPARATOR + "fble" + SEPARATOR + STR_FMT + NL + SEPARATOR + NOP;
    private static final String FBE_OP = SEPARATOR + "fbe" + SEPARATOR + STR_FMT + NL + SEPARATOR + NOP;
    private static final String FBNE_OP = SEPARATOR + "fbne" + SEPARATOR + STR_FMT + NL + SEPARATOR + NOP;
    
    /************************* SECTION ******************************/
    public static final String SECTION = NL + SEPARATOR + ".section" + SEPARATOR + "\"" + STR_FMT + NL;
    private static final String BSS = ".bss\"";
    public static final String TEXT = ".text\"";
    private static final String RODATA = ".rodata\"";
    private static final String SKIP = SEPARATOR + ".skip" + SEPARATOR + INT_FMT + NL;
    private static final String GLOBAL = SEPARATOR + ".global" + SEPARATOR + STR_FMT + NL;
    private static final String ALIGN = SEPARATOR + ".align" + SEPARATOR + INT_FMT + NL;
    private static final String ASCIZ = SEPARATOR + ".asciz" + SEPARATOR + STR_FMT + NL;
    private static final String SINGLE = SEPARATOR + ".single" + SEPARATOR + STR_FMT + NL;
    
    private static final String TWO_PARAM = SEPARATOR + "%s" + SEPARATOR + SEPARATOR + "%s, %s" + NL;
    private static final String THREE_PARAM = SEPARATOR + "%s" + SEPARATOR + "%s, %s, %s" + NL;
    private static final String PRINTF = String.format(CALL_OP, "printf");
    private static final String PRINT_FLOAT = String.format(CALL_OP, "printFloat");
    
    
    private static final String INPUT_INT = String.format(CALL_OP, "inputInt");
    private static final String INPUT_FLOAT = String.format(CALL_OP, "inputFloat");
    
    private StringBuffer rodataSB = new StringBuffer();
    private StringBuffer textSB = new StringBuffer(String.format(SECTION, TEXT) + NL);
    private StringBuffer bssSB = new StringBuffer(String.format(SECTION, BSS) + NL + ".init:" + NL + String.format(SKIP, 4));
    static StringBuffer funcSB = new StringBuffer(); // tmp buffer to store all instructions from current func
    static StringBuffer globalSB = new StringBuffer();
    static boolean inShortCircuit = false; // changed 
    private Stack<StringBuffer> ifStmtLabelStack = new Stack<StringBuffer>();
    private Stack<String> loopLabelStack = new Stack<String>();
    
    public CodeGenerator() {
        try {
            fileWriter = new FileWriter(getFileName());
        } 
        catch (IOException e) {
            System.err.printf(ERROR_IO_CONSTRUCT, getFileName());
            e.printStackTrace();
            System.exit(1);
        }
    }
    
    public void decreaseIndent() {
        indent_level--;
    }
    
    public void dispose() {
        try {
            fileWriter.close();
        } 
        catch (IOException e) {
            System.err.println(ERROR_IO_CLOSE);
            e.printStackTrace();
            System.exit(1);
        }
    }
    
    public void increaseIndent() {
        indent_level++;
    }
    
    public String getModuleName() {
    	return moduleName;
    }
    
    public String getFileName() {
		return fileName;
	}

	public void setFileName(String fileName) {
		this.fileName = fileName;
	}
	
	public StringBuffer getRodataSB() {
		return rodataSB;
	}
	
	public StringBuffer getTextSB() {
		return textSB;
	}

	public String getBssSB() {
		return bssSB.toString();
	}

	public void setBssSB(StringBuffer bssSB) {
		this.bssSB = bssSB;
	}
	
	public StringBuffer getGlobalSB() {
		return globalSB;
	}

	public int getStrCount() {
		return strCount;
	}

	public void setStrCount(int strCount) {
		this.strCount = strCount;
	}
	
	public int getFloatCount() {
		return floatCount;
	}
	
	public void setFloatCount(int floatCount) {
		this.floatCount = floatCount;
	}

	public int getPrintBoolCount() {
		return printBoolCount;
	}

	public void setPrintBoolCount(int printBoolCount) {
		this.printBoolCount = printBoolCount;
	}

	public int getIfStmtCount() {
		return ifStmtCount;
	}

	public void setIfStmtCount(int ifStmtCount) {
		this.ifStmtCount = ifStmtCount;
	}
	
	public int getBranchCount() {
		return branchCount;
	}
	
	public void setBranchCount(int branchCount) {
		this.branchCount = branchCount;
	}

	public int getAndCount() {
		return andCount;
	}

	public void setAndCount(int andCount) {
		this.andCount = andCount;
	}

	public int getOrCount() {
		return orCount;
	}

	public void setOrCount(int orCount) {
		this.orCount = orCount;
	}

	public int getLoopCount() {
		return loopCount;
	}

	public void setLoopCount(int loopCount) {
		this.loopCount = loopCount;
	}

	public static int getStaticCount() {
		return staticCount;
	}

	public void setStaticCount(int newStaticCount) {
		staticCount = newStaticCount;
	}

	public void writeAssembly(String template, String ... params) {
        StringBuilder asStmt = new StringBuilder();	
        for (int i = 0; i < indent_level; i++) {
            asStmt.append(SEPARATOR);
        }
        asStmt.append(String.format(template, (Object[])params));
        
        try {
            fileWriter.write(asStmt.toString());
        } 
        catch (IOException e) {
            System.err.println(ERROR_IO_WRITE);
            e.printStackTrace();
        }
    }
    
    public void writeAssembly(String template, Integer ... params) {
        StringBuilder asStmt = new StringBuilder();
        
        for (int i = 0; i < indent_level; i++) {
            asStmt.append(SEPARATOR);
        }
        
        asStmt.append(String.format(template, (Object[])params));
        
        try {
            fileWriter.write(asStmt.toString());
        } catch (IOException e) {
            System.err.println(ERROR_IO_WRITE);
            e.printStackTrace();
        }
    }
    
    public void writeAssembly(String template) {
        StringBuilder asStmt = new StringBuilder();
        
        for (int i = 0; i < indent_level; i++) {
            asStmt.append(SEPARATOR);
        }
        asStmt.append(template);
        
        try {
            fileWriter.write(asStmt.toString());
        } 
        catch (IOException e) {
            System.err.println(ERROR_IO_WRITE);
            e.printStackTrace();
        }
    }
    
    public void makeComments(String comments, String section) {
    	StringBuffer sb;
    	switch(section) {
    		case "rodata":
    			sb = rodataSB;
    			break;
    		case "global":
    			sb = globalSB;
    			break;
    		case "bss":
    			sb = bssSB;
    			break;
    		case "text":
    			sb = textSB;
    			break;
    		default:
    			sb = funcSB;
    	}
    	sb.append(NL + "!--------" + comments + "---------" + NL);
    }
    
    public void load(String arg1, String arg2, String section) {
    	StringBuffer sb;
    	switch(section) {
    		case "rodata":
    			sb = rodataSB;
    			break;
    		case "global":
    			sb = globalSB;
    			break;
    		case "bss":
    			sb = bssSB;
    			break;
    		case "text":
    			sb = textSB;
    			break;
    		default:
    			sb = funcSB;
    	}
    	sb.append(String.format(SEPARATOR + "ld" + SEPARATOR + SEPARATOR + "[%s], %s" + NL, arg1, arg2));
    }
    
    public void set(String arg1, String arg2, String section) {
    	StringBuffer sb;
    	switch(section) {
    		case "rodata":
    			sb = rodataSB;
    			break;
    		case "global":
    			sb = globalSB;
    			break;
    		case "bss":
    			sb = bssSB;
    			break;
    		case "text":
    			sb = textSB;
    			break;
    		default:
    			sb = funcSB;
    	}
    	sb.append(String.format(TWO_PARAM, SET_OP, arg1, arg2));
    }
    
    public void set(int arg1, String arg2, String section) {
    	StringBuffer sb;
    	switch(section) {
    		case "rodata":
    			sb = rodataSB;
    			break;
    		case "global":
    			sb = globalSB;
    			break;
    		case "bss":
    			sb = bssSB;
    			break;
    		case "text":
    			sb = textSB;
    			break;
    		default:
    			sb = funcSB;
    	}
    	sb.append(String.format(TWO_PARAM, SET_OP, String.valueOf(arg1), arg2));
    }
    
    public void store(String arg1, String arg2, String section) {
    	StringBuffer sb;
    	switch(section) {
    		case "rodata":
    			sb = rodataSB;
    			break;
    		case "global":
    			sb = globalSB;
    			break;
    		case "bss":
    			sb = bssSB;
    			break;
    		case "text":
    			sb = textSB;
    			break;
    		default:
    			sb = funcSB;
    	}
    	sb.append(String.format(SEPARATOR + "st" + SEPARATOR + SEPARATOR + "%s, [%s]" + NL, arg1, arg2));
    }
    
    public void add (String arg1, String arg2, String arg3, String section) {
    	StringBuffer sb;
    	switch(section) {
    		case "rodata":
    			sb = rodataSB;
    			break;
    		case "global":
    			sb = globalSB;
    			break;
    		case "bss":
    			sb = bssSB;
    			break;
    		case "text":
    			sb = textSB;
    			break;
    		default:
    			sb = funcSB;
    	}
    	sb.append(String.format(THREE_PARAM, ADD_OP, arg1, arg2, arg3));
    }
    
    public void call(String funcName, String section) { // marker
    	StringBuffer sb;
    	switch(section) {
    		case "rodata":
    			sb = rodataSB;
    			break;
    		case "global":
    			sb = globalSB;
    			break;
    		case "bss":
    			sb = bssSB;
    			break;
    		case "text":
    			sb = textSB;
    			break;
    		default:
    			sb = funcSB;
    	}
    	sb.append(String.format(SEPARATOR + "call" + SEPARATOR + STR_FMT + NL + SEPARATOR + NOP, funcName));
    }
    
    public void move(String reg1, String reg2, String section) {
    	StringBuffer sb;
    	switch(section) {
    		case "rodata":
    			sb = rodataSB;
    			break;
    		case "global":
    			sb = globalSB;
    			break;
    		case "bss":
    			sb = bssSB;
    			break;
    		case "text":
    			sb = textSB;
    			break;
    		default:
    			sb = funcSB;
    	}
    	sb.append(String.format(TWO_PARAM, MOVE_OP, reg1, reg2));
    }
    
    public void cmp(String reg1, String reg2, String section) {
    	StringBuffer sb;
    	switch(section) {
    		case "rodata":
    			sb = rodataSB;
    			break;
    		case "global":
    			sb = globalSB;
    			break;
    		case "bss":
    			sb = bssSB;
    			break;
    		case "text":
    			sb = textSB;
    			break;
    		default:
    			sb = funcSB;
    	}
    	sb.append(String.format(TWO_PARAM, CMP_OP, reg1, reg2));
    }
    
    public void bge(String label, String section) {
    	StringBuffer sb;
    	switch(section) {
    		case "rodata":
    			sb = rodataSB;
    			break;
    		case "global":
    			sb = globalSB;
    			break;
    		case "bss":
    			sb = bssSB;
    			break;
    		case "text":
    			sb = textSB;
    			break;
    		default:
    			sb = funcSB;
    	}
    	sb.append(String.format(SEPARATOR + "bge" + SEPARATOR + STR_FMT + NL + SEPARATOR + NOP, label));
    }
    
    public void ba(String label, String section) {
    	StringBuffer sb;
    	switch(section) {
    		case "rodata":
    			sb = rodataSB;
    			break;
    		case "global":
    			sb = globalSB;
    			break;
    		case "bss":
    			sb = bssSB;
    			break;
    		case "text":
    			sb = textSB;
    			break;
    		default:
    			sb = funcSB;
    	}
    	sb.append(String.format(SEPARATOR + "ba" + SEPARATOR + SEPARATOR + STR_FMT + NL + SEPARATOR + NOP, label));
    }
    
    public void be(String label, String section) {
    	StringBuffer sb;
    	switch(section) {
    		case "rodata":
    			sb = rodataSB;
    			break;
    		case "global":
    			sb = globalSB;
    			break;
    		case "bss":
    			sb = bssSB;
    			break;
    		case "text":
    			sb = textSB;
    			break;
    		default:
    			sb = funcSB;
    	}
    	sb.append(String.format(SEPARATOR + "be" + SEPARATOR + SEPARATOR + STR_FMT + NL + SEPARATOR + NOP, label));
    }
    public void label(String label, String funcName, boolean isGlobal, String section) {
    	StringBuffer sb;
    	switch(section) {
    		case "rodata":
    			sb = rodataSB;
    			break;
    		case "global":
    			sb = globalSB;
    			break;
    		case "bss":
    			sb = bssSB;
    			break;
    		case "text":
    			sb = textSB;
    			break;
    		default:
    			sb = funcSB;
    	}
    	if(isGlobal) {
    		sb.append(NL + label + ":" + NL);
    	}
    	else {
    		sb.append(NL + "." + funcName + label + ":" + NL);
    	}
    }
    
    public void align(int alignment, String section) {
    	StringBuffer sb;
    	switch(section) {
    		case "rodata":
    			sb = rodataSB;
    			break;
    		case "global":
    			sb = globalSB;
    			break;
    		case "bss":
    			sb = bssSB;
    			break;
    		case "text":
    			sb = textSB;
    			break;
    		default:
    			sb = funcSB;
    	}
    	sb.append(String.format(SEPARATOR + ".align" + SEPARATOR + INT_FMT + NL, alignment));
    }
    
    public String makeLabel(String label, String funcName, boolean isGlobal) {
    	if(isGlobal) {
    		return NL + label + ":" + NL;
    	}
    	else {
    		return NL + "." + funcName + label + ":" + NL;
    	}
    }
    
    public void writeFileHeader() {
        writeAssembly(FILE_HEADER, (new Date()).toString());
        increaseIndent();
        makeComments("Default String Formatters", "rodata");
    	rodataSB.append(String.format(SECTION, RODATA));
    	rodataSB.append(String.format(ALIGN, 4));
    	rodataSB.append(makeLabel("intfmt", "", false));
    	rodataSB.append(String.format(ASCIZ, HEADER_INT));
    	rodataSB.append(makeLabel("strfmt", "", false));
    	rodataSB.append(String.format(ASCIZ, HEADER_STRING));
    	rodataSB.append(makeLabel("endl", "", false));
    	rodataSB.append(String.format(ASCIZ, HEADER_NL));
    	rodataSB.append(makeLabel("bool_true", "", false));
    	rodataSB.append(String.format(ASCIZ, HEADER_BOOL_TRUE));
    	rodataSB.append(makeLabel("bool_false", "", false));
    	rodataSB.append(String.format(ASCIZ, HEADER_BOOL_FALSE));
    	rodataSB.append(String.format(ALIGN, 4));
    	rodataSB.append(makeLabel("float_one", "", false));
    	rodataSB.append(String.format(SINGLE, "0r1.0"));
    	decreaseIndent();
    }

	public void printConstant(STO sto, String funcName) {
		makeComments("Printing Constant: " + sto.getName(), "func");
		Type stoType = sto.getType();
		String stoName = sto.getName();
		if(stoType.isInt()) {
			funcSB.append(String.format(TWO_PARAM, SET_OP, ".intfmt", OUT_0));
			funcSB.append(String.format(TWO_PARAM, SET_OP, ((ConstSTO)sto).getIntValue(), OUT_1));
			funcSB.append(PRINTF);
		}
		else if(stoType.isFloat()) {
			String label = floatNameGenerator();
			rodataSB.append(String.format(ALIGN, 4)); // have to be in front of label?
			rodataSB.append(makeLabel(label, funcName, false));
			rodataSB.append(String.format(SINGLE, "0r" + ((ConstSTO)sto).getFloatValue()));
			funcSB.append(String.format(TWO_PARAM, SET_OP, "." + funcName + label, LOCAL_0));
			load(LOCAL_0, FLOAT_0, "func");
			funcSB.append(PRINT_FLOAT);
		}
		else if(stoType.isBool()) {
			if (sto.hasAddress()) {
				funcSB.append(String.format(TWO_PARAM, SET_OP, ".strfmt", OUT_0));
				funcSB.append(String.format(TWO_PARAM, SET_OP, sto.getOffset(), LOCAL_1));
				funcSB.append(String.format(THREE_PARAM, ADD_OP, sto.getBase(), LOCAL_1, LOCAL_1));
				load(LOCAL_1, LOCAL_1, "func");
				funcSB.append(String.format(TWO_PARAM, CMP_OP, 0, LOCAL_1));
				funcSB.append(String.format(BE_OP, ".print_false" + getPrintBoolCount()));
				funcSB.append(String.format(TWO_PARAM, SET_OP, ".bool_true", OUT_1));
				funcSB.append(String.format(BA_OP, ".next"+ getPrintBoolCount()));
				funcSB.append(makeLabel("print_false" + getPrintBoolCount(), "", false));
				funcSB.append(String.format(TWO_PARAM, SET_OP, ".bool_false", OUT_1));
				funcSB.append(makeLabel("next"+ getPrintBoolCount(), "", false));
				funcSB.append(PRINTF);
				setPrintBoolCount(getPrintBoolCount()+1) ;
			}
			else {
				funcSB.append(String.format(TWO_PARAM, SET_OP, ".strfmt", OUT_0));
				if(((ConstSTO)sto).getIntValue() == 1) {
					funcSB.append(String.format(TWO_PARAM, SET_OP, ".bool_true", OUT_1));
				}
				else {
					funcSB.append(String.format(TWO_PARAM, SET_OP, ".bool_false", OUT_1));
				}
				funcSB.append(PRINTF);
			}
		}
		else {	// String
			String label;
			if(stoName.equals("NL"))
				label = "endl";
			else {
				label = stringNameGenerator();
				rodataSB.append(makeLabel(label, "", false));
				rodataSB.append(String.format(ASCIZ, "\"" + stoName + "\""));
			}
			funcSB.append(String.format(TWO_PARAM, SET_OP, ".strfmt", OUT_0));
			funcSB.append(String.format(TWO_PARAM, SET_OP, "."+label, OUT_1));
			funcSB.append(PRINTF);
		}
    }
	
	public void printNonConstant(STO sto) {
		makeComments("Printing Non-Constant: " + sto.getType().getName(), "func");
		if(sto.getType().isInt()) {
			funcSB.append(String.format(TWO_PARAM, SET_OP, ".intfmt", OUT_0));
			funcSB.append(String.format(TWO_PARAM, SET_OP, sto.getOffset(), LOCAL_0));
			funcSB.append(String.format(THREE_PARAM, ADD_OP, sto.getBase(), LOCAL_0, LOCAL_0));
			if (sto.isRef()) { // check if it is a parameter
				load(LOCAL_0, LOCAL_0, "func");	
			}
			load(LOCAL_0, OUT_1, "func");
			funcSB.append(PRINTF);
		}
		else if(sto.getType().isFloat()) {
			funcSB.append(String.format(TWO_PARAM, SET_OP, sto.getOffset(), LOCAL_0));
			funcSB.append(String.format(THREE_PARAM, ADD_OP, sto.getBase(), LOCAL_0, LOCAL_0));
			if (sto.isRef()) { // check if it is a parameter
				load(LOCAL_0, LOCAL_0, "func");	
			}
			load(LOCAL_0, FLOAT_0, "func");
			funcSB.append(PRINT_FLOAT);
		}
		else if(sto.getType().isBool()){	// boolType
			funcSB.append(String.format(TWO_PARAM, SET_OP, ".strfmt", OUT_0));
			funcSB.append(String.format(TWO_PARAM, SET_OP, sto.getOffset(), LOCAL_1));
			funcSB.append(String.format(THREE_PARAM, ADD_OP, sto.getBase(), LOCAL_1, LOCAL_1));
			if (sto.isRef()) { // check if it is a parameter
				load(LOCAL_1, LOCAL_1, "func");	
			}
			load(LOCAL_1, LOCAL_1, "func");
			funcSB.append(String.format(TWO_PARAM, CMP_OP, 0, LOCAL_1));
			funcSB.append(String.format(BE_OP, ".print_false" + getPrintBoolCount()));
			funcSB.append(String.format(TWO_PARAM, SET_OP, ".bool_true", OUT_1));
			funcSB.append(String.format(BA_OP, ".next"+ getPrintBoolCount()));
			funcSB.append(makeLabel("print_false" + getPrintBoolCount(), "", false));
			funcSB.append(String.format(TWO_PARAM, SET_OP, ".bool_false", OUT_1));
			funcSB.append(makeLabel("next"+ getPrintBoolCount(), "", false));
			funcSB.append(PRINTF);
			setPrintBoolCount(getPrintBoolCount()+1) ;
		}
	}
	
	public String stringNameGenerator() {
		String s = "str" + Integer.toString(getStrCount());
		setStrCount(getStrCount() + 1);
		return s;
	}
	
	public String floatNameGenerator() {
		String s = "float" + Integer.toString(getFloatCount());
		setFloatCount(getFloatCount() + 1);
		return s;
	}
	
	public void printFuncStart(String funcName, boolean isStruct, String structName) {
		makeComments(funcName, "text");
		textSB.append(String.format(ALIGN, 4));
		if(isStruct) {
			textSB.append("." + structName + "." + funcName + ":" + NL);
			textSB.append(String.format(TWO_PARAM, SET_OP, "SAVE." + structName +  "." + funcName, GLOBAL_1));
		}
		else {
			textSB.append(String.format(GLOBAL, funcName) + NL);
			textSB.append(funcName+":"+NL);
			textSB.append(String.format(TWO_PARAM, SET_OP, "SAVE." + funcName, GLOBAL_1));
		}
		textSB.append(String.format(THREE_PARAM, SAVE_OP, SP, GLOBAL_1, SP));
		if(funcName.equals("main")) { // print init Guard 
			printGlobalInitGuard();
			textSB.append(globalSB.toString());
			textSB.append(makeLabel("init_done", "", false));
		}
	}
	
	public void printGlobalInitGuard() {
		makeComments("Global Initialization Guard", "text");
		textSB.append(String.format(TWO_PARAM, SET_OP, ".init", LOCAL_0));
		load(LOCAL_0, LOCAL_1, "text");
		textSB.append(String.format(TWO_PARAM, CMP_OP, LOCAL_1, GLOBAL_0));
		textSB.append(String.format(BNE_OP, ".init_done"));
		textSB.append(String.format(TWO_PARAM, MOVE_OP, 1, LOCAL_1));
		textSB.append(String.format(STORE_OP, LOCAL_1, LOCAL_0));
	}
	
	public void printLocalStaticInitGuard(String funcName, String varName, int offset) {
		makeComments("Local Static Initialization Guard", "func");
		funcSB.append(String.format(TWO_PARAM, SET_OP, "." + funcName + varName + getStaticCount(), LOCAL_0));
		funcSB.append(String.format(THREE_PARAM, ADD_OP, LOCAL_0, String.valueOf(offset), LOCAL_0));
		load(LOCAL_0, LOCAL_1, "func");
		funcSB.append(String.format(TWO_PARAM, CMP_OP, LOCAL_1, GLOBAL_0));
		funcSB.append(String.format(BNE_OP, ".init_done_"+ funcName + varName + getStaticCount()));
		funcSB.append(String.format(TWO_PARAM, MOVE_OP, 1, LOCAL_1));
		funcSB.append(String.format(STORE_OP, LOCAL_1, LOCAL_0));
	}
	
	public void printFuncEnd(FuncSTO funcSTO, int size, boolean isStruct, String structName) {
		if(funcSTO.getReturnType().isVoid()) {
			funcSB.append(RETURN);
			funcSB.append(RESTORE);
		}
		if(isStruct) {
			funcSB.append(printSave(structName + "." + funcSTO.getName(), size));
		}
		else {
			funcSB.append(printSave(funcSTO.getName(), size));
		}
		textSB.append(funcSB.toString()); // insert behind ".section .text"
		funcSB.delete(0, funcSB.length());	// clear current func buffer for a new function
	}
	
	public String printSave(String funcName, int size) { // size = size of total local variable
		return String.format("SAVE." + funcName + "=" + "-(92 + " + size + ") & -8" + NL + NL);
	}
	
	
	public void printDoCallNoParam(ExprSTO exprSTO, STO des, int localVarSize){
		StringBuffer sb = funcSB;
		if(des.getStructName() != null) {
			sb.append(String.format(CALL_OP, des.getStructName()));
		}
		else {
			sb.append(String.format(CALL_OP, des.getName()));	// funcPtrType
		}
		if(!(exprSTO.getType().isVoid())) {
			// set offset
			sb.append(String.format(TWO_PARAM, SET_OP, "-" + String.valueOf(localVarSize), LOCAL_0));
			sb.append(String.format(THREE_PARAM, ADD_OP, FP, LOCAL_0, LOCAL_0));
			if(exprSTO.getType().isFloat() && !((FuncPtrType) des.getType()).isReturnByRef()) {
				sb.append(String.format(STORE_OP, FLOAT_0, LOCAL_0));
			}
			else {
				sb.append(String.format(STORE_OP, OUT_1, LOCAL_0));
			}
		}
	}
	
	public void printDoCallWithParam(ExprSTO exprSTO, STO des, int localVarSize, Vector<STO> argList, Vector<STO> params){
		String section = "func";	// a function call must be done locally
		makeComments("Printing Calling function: " + des.getName(), section);
		int tmpSPOffset = 0;
		for(int i = 0; i < argList.size(); i++) {
			STO arg = argList.get(i);
			VarSTO param = (VarSTO) params.get(i);
			if(param.isRef()) {
				set(arg.getOffset(), LOCAL_0, section);
				add(arg.getBase(), LOCAL_0, LOCAL_0, section);
				if (arg.isRef()) { // check if it is a parameter
					load(LOCAL_0, LOCAL_0, "func");	
				}
				funcSB.append(String.format(THREE_PARAM, ADD_OP, SP, String.valueOf(72 + tmpSPOffset), LOCAL_1));
				funcSB.append(String.format(STORE_OP, LOCAL_0, LOCAL_1));
			}
			else {
				if(arg.hasAddress()) {
					funcSB.append(String.format(TWO_PARAM, SET_OP, arg.getOffset(), LOCAL_0));
					funcSB.append(String.format(THREE_PARAM, ADD_OP, arg.getBase(), LOCAL_0, LOCAL_0));
					if (arg.isRef()) { // check if it is a parameter
						load(LOCAL_0, LOCAL_0, "func");	
					}
					if(arg.getType().isInt() && param.getType().isFloat()) {
						MyParser.setLocalVarSize(MyParser.getLocalVarSize() + 4);
						int offset = MyParser.getLocalVarSize();
						load(LOCAL_0, LOCAL_0, "func");
						funcSB.append(String.format(TWO_PARAM, SET_OP, "-" + String.valueOf(offset), LOCAL_1)) ;
						funcSB.append(String.format(THREE_PARAM, ADD_OP, FP, LOCAL_1, LOCAL_1)) ;
						
						funcSB.append(String.format(STORE_OP, LOCAL_0, LOCAL_1));
						load(LOCAL_1, FLOAT_1, "func");
						funcSB.append(String.format(TWO_PARAM, FITOS_OP, FLOAT_1, FLOAT_1)); // change bit pattern
						funcSB.append(String.format(THREE_PARAM, ADD_OP, SP, String.valueOf(72 + tmpSPOffset), LOCAL_0));
						funcSB.append(String.format(STORE_OP, FLOAT_1, LOCAL_0));
					}
					else {
						funcSB.append(String.format(THREE_PARAM, ADD_OP, SP, String.valueOf(72 + tmpSPOffset), LOCAL_1));
						load(LOCAL_0, LOCAL_0, "func");
						funcSB.append(String.format(STORE_OP, LOCAL_0, LOCAL_1));
					}
				}
				else {	// const literal
					funcSB.append(String.format(THREE_PARAM, ADD_OP, SP, String.valueOf(72 + tmpSPOffset), LOCAL_0));
					if(arg.getType().isInt() || arg.getType().isBool()) {
						if (arg.getType().isInt() && param.getType().isFloat()) {
							funcSB.append(String.format(TWO_PARAM, SET_OP, ((ConstSTO)arg).getIntValue(), LOCAL_0));
							MyParser.setLocalVarSize(MyParser.getLocalVarSize() + 4);
							int offset = MyParser.getLocalVarSize();
							funcSB.append(String.format(TWO_PARAM, SET_OP, "-" + String.valueOf(offset), LOCAL_1));
							funcSB.append(String.format(THREE_PARAM, ADD_OP, FP, LOCAL_1, LOCAL_1)) ;
							
							funcSB.append(String.format(STORE_OP, LOCAL_0, LOCAL_1));
							load(LOCAL_1, FLOAT_1, "func");
							funcSB.append(String.format(TWO_PARAM, FITOS_OP, FLOAT_1, FLOAT_1)); // change bit pattern
							funcSB.append(String.format(THREE_PARAM, ADD_OP, SP, String.valueOf(72 + tmpSPOffset), LOCAL_0));
							funcSB.append(String.format(STORE_OP, FLOAT_1, LOCAL_0));
						} 
						else {
							funcSB.append(String.format(TWO_PARAM, SET_OP, ((ConstSTO)arg).getIntValue(), LOCAL_1));
							funcSB.append(String.format(STORE_OP, LOCAL_1, LOCAL_0));
						}
					}
					else {
						String label = floatNameGenerator(); 
						rodataSB.append(String.format(ALIGN, 4));
						rodataSB.append(makeLabel(label, "", false));
						rodataSB.append(String.format(SINGLE, "0r" + ((ConstSTO)arg).getFloatValue()));
						funcSB.append(String.format(TWO_PARAM, SET_OP, "."+ label, LOCAL_1));
						load(LOCAL_1, FLOAT_1, "func");
						funcSB.append(String.format(STORE_OP, FLOAT_1, LOCAL_0)); // should that be LOCAL_1 instead of label?
					}
				}
			}
			tmpSPOffset += arg.getType().getSize();
		}
		funcSB.append(String.format(CALL_OP, des.getName()));	// funcPtrType
		// store func return result onto the stack
		if(!(exprSTO.getType().isVoid())) {
			funcSB.append(String.format(TWO_PARAM, SET_OP, "-" + String.valueOf(localVarSize), LOCAL_0));
			funcSB.append(String.format(THREE_PARAM, ADD_OP, FP, LOCAL_0, LOCAL_0));
			if(exprSTO.getType().isFloat() && !exprSTO.isModLValue()) { //exprSTO.isModLValue(): if true, it means it is return by Ref
				funcSB.append(String.format(STORE_OP, FLOAT_0, LOCAL_0));
			}
			else {
				funcSB.append(String.format(STORE_OP, OUT_1, LOCAL_0));
			}
		}
	}
	
	/****************************** PHASE IA: VARIABLE DECLARATION **************************/
	public void printVarDecl(STO sto, STO initVal, String funcName, boolean isStatic) {
		StringBuffer sb;
		String section;
		boolean localStaticFlag = false;
		if(sto.isGlobal()) {
			sb = globalSB;
			section = "global";
			makeComments("Declaring variable: " + sto.getName(), section);
			if(!isStatic) {
				bssSB.append(String.format(GLOBAL, sto.getName()));
			}
			bssSB.append(makeLabel(sto.getName(), funcName, true));
			bssSB.append(String.format(SKIP, sto.getType().getSize()));
		}
		else {
			sb = funcSB;
			section = "func";
			makeComments("Declaring variable: " + sto.getName(), section);
			if(isStatic) {
				bssSB.append(makeLabel(sto.getName()+getStaticCount(), funcName, false));
				bssSB.append(String.format(SKIP, sto.getType().getSize()));
				bssSB.append(String.format(SKIP, sto.getType().getSize())); // for local static initguard
				printLocalStaticInitGuard(funcName, sto.getName(), sto.getType().getSize());
				localStaticFlag = true;
			}
		}
		if(initVal != null) {
			makeComments("Initializations: " + sto.getName() +" = " + initVal.getName(), section);
			if(initVal.isAddressOfTypeCast()) {
				sto.setAddressOfTypeCast(true);
			}
			if(!initVal.hasAddress()) {
				if (initVal.getType().isInt()) {
					if (sto.getType().isFloat()) {	// left is float but right is int ..Need to FITOS
						// make label
						String label = floatNameGenerator();
						rodataSB.append(String.format(ALIGN, 4));
						rodataSB.append(makeLabel(label, "", false));
						rodataSB.append(String.format(SINGLE, "0r" + ((ConstSTO)initVal).getFloatValue()));
						sb.append(String.format(TWO_PARAM, SET_OP, "."+label, LOCAL_1));
						load(LOCAL_1, FLOAT_1, section);
					} 
					else { // left is int and right is also int  
						sb.append(String.format(TWO_PARAM, SET_OP, ((ConstSTO)initVal).getIntValue(), LOCAL_0));						
					} // marker
				}
				else if (initVal.getType().isFloat()) {
					String label = floatNameGenerator();
					rodataSB.append(String.format(ALIGN, 4));
					rodataSB.append(makeLabel(label, "", false));
					rodataSB.append(String.format(SINGLE, "0r" + ((ConstSTO)initVal).getFloatValue()));
					sb.append(String.format(TWO_PARAM, SET_OP, "."+label, LOCAL_1));
					load(LOCAL_1, FLOAT_1, section);
				} 
				else if (initVal.getType().isBool()){
					sb.append(String.format(TWO_PARAM, SET_OP, ((ConstSTO)initVal).getIntValue(), LOCAL_0));
				}
			} 
			else { // initVal is ExprSTO or VarSTO
				sb.append(String.format(TWO_PARAM, SET_OP, initVal.getOffset(), LOCAL_0));
				sb.append(String.format(THREE_PARAM, ADD_OP, initVal.getBase(), LOCAL_0, LOCAL_0));
				if (initVal.getType().isInt()) {
					if(initVal.isRef()) {
						load(LOCAL_0, LOCAL_0, section);
					}
					if (sto.getType().isInt()) {
						load(LOCAL_0, LOCAL_0, section);
					}
					else {
						load(LOCAL_0, FLOAT_1, section);	// marker
						sb.append(String.format(TWO_PARAM, FITOS_OP, FLOAT_1, FLOAT_1)); // change bit pattern
					}
				} 
				else if (initVal.getType().isPointerType()) {
					System.out.println("Var decl");
					System.out.println(initVal.getName() + "ref: " + initVal.isRef() + " addressof: " + initVal.isAddressOfTypeCast());
					if(initVal.getType().isPointerType() && !initVal.isAddressOfTypeCast()) {
						load(LOCAL_0, LOCAL_0, section);
					}
					if(initVal.isRef() || initVal.isAddressOfTypeCast()) {
						load(LOCAL_0, LOCAL_0, section);
					}
				}
				else if (initVal.getType().isFloat()) { // float type
					if(initVal.isRef()) {
						load(LOCAL_0, LOCAL_0, section);
					}
					load(LOCAL_0, FLOAT_1, section);
				} 
				else if (initVal.getType().isBool()){
					if(initVal.isRef()) {
						load(LOCAL_0, LOCAL_0, section);
					}
					load(LOCAL_0, LOCAL_0, section);
				}
				else if(initVal.getType().isStructType()) {
					makeComments("Call memcpy for left and right", section);
					if(initVal.isRef()) {
						load(LOCAL_0, LOCAL_0, section);
					}
					set(sto.getOffset(), LOCAL_0, section);
					add(sto.getBase(), LOCAL_0, LOCAL_0,section);
					if(sto.isRef()) {
						load(LOCAL_0, OUT_0, section);
					}
					else {
						move(LOCAL_0, OUT_0, section);
					}
					set(initVal.getOffset(),LOCAL_0,section);
					add(initVal.getBase(),LOCAL_0,LOCAL_0,section);
					if(initVal.isRef()) {
						load(LOCAL_0, OUT_1, section);
					}
					else {
						move(LOCAL_0, OUT_1, section);
					}
					set(initVal.getType().getSize(), OUT_2, section);
					call("memcpy", section);
				}
			}
			// load address of where the VarSTO is located to %l1
			sb.append(String.format(TWO_PARAM, SET_OP, sto.getOffset(), LOCAL_1));
			sb.append(String.format(THREE_PARAM, ADD_OP, sto.getBase(), LOCAL_1, LOCAL_1));
			if (sto.getType().isFloat()) {
				sb.append(String.format(STORE_OP, FLOAT_1, LOCAL_1));
			} 
			else {
				sb.append(String.format(STORE_OP, LOCAL_0, LOCAL_1));
			}
		}
		if(localStaticFlag) {
			sb.append(NL + ".init_done_" + funcName + sto.getName() + getStaticCount() + ":" + NL);
			setStaticCount(getStaticCount() + 1);
		}
	}

	//-------------------------------------------------------------------------------------------
	// printBinaryExpr(STO left, Operator o, STO right, String base, String offset)
	// - Added in Phase I.2
	// - Evaluate binary expressions including arithmetic, relational, and logical operations 
	//   where either left or right is not a ConstSTO (otherwise constant folding would have
	//   already handled it)
	//-------------------------------------------------------------------------------------------
	public void printBinaryExpr(STO left, Operator o, STO right, STO result, boolean isGlobal) {
		StringBuffer sb;
		String scope;
		String section;
		String base = result.getBase();
		String offset = result.getOffset();
		if(isGlobal) {
			sb = globalSB;
			section = "global";
			scope = "Global ";
		}
		else {
			sb = funcSB;
			section = "func";
			scope = "Local ";
		}
		if(o.isBooleanOp()) {
			printShortCircuitRight(right, o, isGlobal, base, offset);
			return;
		}
		makeComments(scope + left.getName() + o.getOpName() + right.getName(), section);
		// if either left or right is a float, we want to separate the logic from int and bool
		// to keep this method simple and clean
		if(left.getType().isFloat() || right.getType().isFloat()) {
			printBinaryFloatExpr(left, right, o, isGlobal, base, offset);
			return; // printBinaryFloatExpr include everything for FLOAT
		}
		else {
			if(left.hasAddress()) { //!left.isConst()
				sb.append(String.format(TWO_PARAM, SET_OP, left.getOffset(), LOCAL_0));
				sb.append(String.format(THREE_PARAM, ADD_OP, left.getBase(), LOCAL_0, LOCAL_0));
				if(left.isRef()) {
					load(LOCAL_0, LOCAL_0, section);
				}
				load(LOCAL_0, LOCAL_0, section);
				if(right.hasAddress()) { // !right.isConst()
					sb.append(String.format(TWO_PARAM, SET_OP, right.getOffset(), LOCAL_1));
					sb.append(String.format(THREE_PARAM, ADD_OP, right.getBase(), LOCAL_1, LOCAL_1));
					if(right.isRef()) {
						load(LOCAL_1, LOCAL_1, section);
					}
					load(LOCAL_1, LOCAL_1, section);
				}
				else {
					if(right.getType().isNullPtrType()) {
						sb.append(String.format(TWO_PARAM, SET_OP, 0, LOCAL_1));
					}
					else {
						sb.append(String.format(TWO_PARAM, SET_OP, ((ConstSTO)right).getIntValue(), LOCAL_1));
					}
				}
			}
			else {	// ConstSTO, right must be VarSTO/ExprSTO
				if(left.getType().isNullPtrType()) {
					sb.append(String.format(TWO_PARAM, SET_OP, 0, LOCAL_0));
				}
				else {
					sb.append(String.format(TWO_PARAM, SET_OP, ((ConstSTO)left).getIntValue(), LOCAL_0));
				}
				if(!right.hasAddress()) {
					sb.append(String.format(TWO_PARAM, SET_OP, ((ConstSTO)right).getIntValue(), LOCAL_1));
				}
				else {
					sb.append(String.format(TWO_PARAM, SET_OP, right.getOffset(), LOCAL_1));
					sb.append(String.format(THREE_PARAM, ADD_OP, right.getBase(), LOCAL_1, LOCAL_1));
				}
				if(right.isRef()) {
					load(LOCAL_1, LOCAL_1, section);
				}
				load(LOCAL_1, LOCAL_1, section);
			}
			if(o.isAddOp()) {
				sb.append(String.format(THREE_PARAM, ADD_OP, LOCAL_0, LOCAL_1, LOCAL_0));
			}
			else if(o.isMulOp()) {
				sb.append(String.format(TWO_PARAM, MOVE_OP, LOCAL_0, OUT_0));
				sb.append(String.format(TWO_PARAM, MOVE_OP, LOCAL_1, OUT_1));
				sb.append(String.format(CALL_OP, ".mul"));
				sb.append(String.format(TWO_PARAM, MOVE_OP, OUT_0, LOCAL_0));
			}
			else if(o.isDivOp()) {
				sb.append(String.format(TWO_PARAM, MOVE_OP, LOCAL_0, OUT_0));
				sb.append(String.format(TWO_PARAM, MOVE_OP, LOCAL_1, OUT_1));
				sb.append(String.format(CALL_OP, ".div"));
				sb.append(String.format(TWO_PARAM, MOVE_OP, OUT_0, LOCAL_0));
			}
			else if(o.isModOp()) {
				sb.append(String.format(TWO_PARAM, MOVE_OP, LOCAL_0, OUT_0));
				sb.append(String.format(TWO_PARAM, MOVE_OP, LOCAL_1, OUT_1));
				sb.append(String.format(CALL_OP, ".rem"));
				sb.append(String.format(TWO_PARAM, MOVE_OP, OUT_0, LOCAL_0));
			}
			else if(o.isMinusOp()) {
				sb.append(String.format(THREE_PARAM, SUB_OP, LOCAL_0, LOCAL_1, LOCAL_0));
			}
			else if(o.isBitwiseOp()) {
				if(o.isAmpersandOp()) {
					sb.append(String.format(THREE_PARAM, AND_OP, LOCAL_0, LOCAL_1, LOCAL_0));
				}
				else if(o.isBarOp()) {
					sb.append(String.format(THREE_PARAM, OR_OP, LOCAL_0, LOCAL_1, LOCAL_0));
				}
				else { // CaretOp ^
					sb.append(String.format(THREE_PARAM, XOR_OP, LOCAL_0, LOCAL_1, LOCAL_0));
				}
			}
			else if(o.isComparisonOp()) {
				sb.append(String.format(TWO_PARAM, CMP_OP, LOCAL_0, LOCAL_1));
				String label;
				String op;
				if(o.isGreaterThanOp()) { // >
					label = "BG";
					op = BG_OP;
				} 
				else if(o.isGreaterThanOrEqualToOp()) { // >=
					label = "BGE";
					op = BGE_OP;
				}
				else if(o.isLessThanOp()) { // >=
					label = "BLE";
					op = BL_OP;
				}
				else if (o.isLessThanOrEqualToOp()){ // <=
					label = "LE";
					op = BLE_OP;
				}
				else if (o.isEqualToOp()){ // == 
					label = "BE";
					op = BE_OP;
				}
				else { // !=
					label = "BNE";
					op = BNE_OP;
				}
				sb.append(String.format(op, "." + label + "_"+String.valueOf(getBranchCount())));
				sb.append(String.format(TWO_PARAM, MOVE_OP, 0, LOCAL_0));
				sb.append(String.format(BA_OP, ".DONE_" + label + "_" + getBranchCount()));
				sb.append(makeLabel(label + "_"+String.valueOf(getBranchCount()), "", false));
				sb.append(String.format(TWO_PARAM, MOVE_OP, 1, LOCAL_0));
				sb.append(makeLabel("DONE_" + label + "_"+String.valueOf(getBranchCount()), "", false));
				setBranchCount(getBranchCount() + 1);
			}
		}
		sb.append(String.format(TWO_PARAM, SET_OP, offset, LOCAL_1));
		sb.append(String.format(THREE_PARAM, ADD_OP, base, LOCAL_1, LOCAL_1));
		sb.append(String.format(STORE_OP, LOCAL_0, LOCAL_1));
	}
	
	public void printBinaryFloatExpr(STO left, STO right, Operator o, boolean isGlobal, String base, String offset) {
		StringBuffer sb;
		String section;
		if(isGlobal) {
			sb = globalSB;
			section = "global";
		}
		else {
			sb = funcSB;
			section = "func";
		}
		if(left.isConst()) {	
			// right is a VarSTO/ExprSTO for sure because otherwise would have done constant folding
			if(left.getType().isFloat()) {
				String label = floatNameGenerator();
				rodataSB.append(String.format(ALIGN, 4));
				rodataSB.append(makeLabel(label, "", false));
				rodataSB.append(String.format(SINGLE, "0r" + ((ConstSTO)left).getFloatValue()));
				sb.append(String.format(TWO_PARAM, SET_OP, "."+ label, LOCAL_0));
				load(LOCAL_0, FLOAT_1, section);
			} 
			else { // left is int type
				sb.append(String.format(TWO_PARAM, SET_OP, ((ConstSTO)left).getIntValue(), LOCAL_0));
				MyParser.setLocalVarSize(MyParser.getLocalVarSize() + 4);
				String tmpOffset = "-" + String.valueOf(MyParser.getLocalVarSize());
				set(tmpOffset, LOCAL_1, section);
				add(FP, LOCAL_1, LOCAL_1, section);
				sb.append(String.format(STORE_OP, LOCAL_0, LOCAL_1));
				load(LOCAL_1, FLOAT_1, section);
				sb.append(String.format(TWO_PARAM, FITOS_OP, FLOAT_1, FLOAT_1)); // change bit pattern
			}
			// Right is VarSTO/ EXPRSTO 
			sb.append(String.format(TWO_PARAM, SET_OP, right.getOffset(), LOCAL_1));
			sb.append(String.format(THREE_PARAM, ADD_OP, right.getBase(), LOCAL_1, LOCAL_1));
			if(right.isRef()) {
				load(LOCAL_1, LOCAL_1, section);
			}
			load(LOCAL_1, FLOAT_2, section);
			if (right.getType().isInt()) {
				sb.append(String.format(TWO_PARAM, FITOS_OP, FLOAT_2, FLOAT_2));		
			}
			
		}
		else {	// left is a VarSTO/ExprSTO
			sb.append(String.format(TWO_PARAM, SET_OP, left.getOffset(), LOCAL_0));
			sb.append(String.format(THREE_PARAM, ADD_OP, left.getBase(), LOCAL_0, LOCAL_0));
			if(left.isRef()) {
				load(LOCAL_0, LOCAL_0, section);
			}
			load(LOCAL_0, FLOAT_1, section);
			if(left.getType().isInt()) {	// FITOS
				sb.append(String.format(TWO_PARAM, FITOS_OP, FLOAT_1, FLOAT_1));
			}
			if(right.isConst()) {
				if(right.getType().isInt()) {
					sb.append(String.format(TWO_PARAM, SET_OP, ((ConstSTO)right).getIntValue(), LOCAL_0));
					MyParser.setLocalVarSize(MyParser.getLocalVarSize() + 4) ;
					sb.append(String.format(THREE_PARAM, ADD_OP, FP, "-" + String.valueOf(MyParser.getLocalVarSize()), LOCAL_1));
					sb.append(String.format(STORE_OP, LOCAL_0, LOCAL_1));
					load(LOCAL_1, FLOAT_2, section);
					sb.append(String.format(TWO_PARAM, FITOS_OP, FLOAT_2, FLOAT_2)); 
				}
				else {
					String label = floatNameGenerator();
					rodataSB.append(String.format(ALIGN, 4));
					rodataSB.append(makeLabel(label, "", false));
					rodataSB.append(String.format(SINGLE, "0r" + ((ConstSTO)right).getFloatValue()));
					sb.append(String.format(TWO_PARAM, SET_OP, "."+label, LOCAL_0));
					load(LOCAL_0, FLOAT_2, section);
				}
			}
			else { // left and right are exprSTO/varSTO
				sb.append(String.format(TWO_PARAM, SET_OP, right.getOffset(), LOCAL_0));
				sb.append(String.format(THREE_PARAM, ADD_OP, right.getBase(), LOCAL_0, LOCAL_0));
				if(right.isRef()) {
					load(LOCAL_0, LOCAL_0, section);
				}
				load(LOCAL_0, FLOAT_2, section);
				if(right.getType().isInt()) {
					sb.append(String.format(TWO_PARAM, FITOS_OP, FLOAT_2, FLOAT_2));
				}
			}
		}
		if (o.isAddOp()) {
			sb.append(String.format(THREE_PARAM, FADDS_OP, FLOAT_1, FLOAT_2, FLOAT_1));
		}
		else if(o.isMinusOp()) {
			sb.append(String.format(THREE_PARAM, FSUBS_OP, FLOAT_1, FLOAT_2, FLOAT_1));
		}
		else if(o.isMulOp()) {
			sb.append(String.format(THREE_PARAM, FMULS_OP, FLOAT_1, FLOAT_2, FLOAT_1));
		}
		else if(o.isDivOp()) {
			sb.append(String.format(THREE_PARAM, FDIVS_OP, FLOAT_1, FLOAT_2, FLOAT_1));
		}			
		else if(o.isComparisonOp()) {
			String label;
			String op;
			sb.append(String.format(TWO_PARAM, FCMPS_OP, FLOAT_1, FLOAT_2) + SEPARATOR + NOP);
			if(o.isGreaterThanOp()) {
				label = "FBG";
				op = FBG_OP;
			}
			else if(o.isGreaterThanOrEqualToOp()) { // >=
				label = "FBGE";
				op = FBGE_OP;
			}
			else if(o.isLessThanOp()) { // >=
				label = "FBLE";
				op = FBL_OP;
			}
			else if (o.isLessThanOrEqualToOp()){ // <=
				label = "FLE";
				op = FBLE_OP;
			}
			else if (o.isEqualToOp()){ // == 
				label = "FBE";
				op = FBE_OP;
			}
			else { // !=
				label = "FBNE";
				op = FBNE_OP;
			}
			sb.append(String.format(op, "." + label + "_"+String.valueOf(getBranchCount())));
			sb.append(String.format(TWO_PARAM, MOVE_OP, 0, LOCAL_0));
			sb.append(String.format(BA_OP, ".DONE_" + label + "_"+getBranchCount()));
			sb.append(makeLabel(label + "_"+String.valueOf(getBranchCount()), "", false));
			sb.append(String.format(TWO_PARAM, MOVE_OP, 1, LOCAL_0));
			sb.append(makeLabel("DONE_" + label + "_"+String.valueOf(getBranchCount()), "", false));
			setBranchCount(getBranchCount() + 1);
		}
		sb.append(String.format(TWO_PARAM, SET_OP, offset, LOCAL_1));
		sb.append(String.format(THREE_PARAM, ADD_OP, base, LOCAL_1, LOCAL_1));
		if(o.isComparisonOp()) {
			sb.append(String.format(STORE_OP, LOCAL_0, LOCAL_1));
		}
		else {
			sb.append(String.format(STORE_OP, FLOAT_1, LOCAL_1));
		}
	} 
	
	public void printShortCircuitLeft(STO left, Operator o, boolean isGlobal) {
		String section;
		if(isGlobal) {
			section = "global";
		}
		else {
			section = "func";
		} 
		makeComments("In short-circuit left " + left.getName(), section);
		if(!left.hasAddress()) {
			set(String.valueOf(((ConstSTO)left).getIntValue()), LOCAL_0, section);
		}
		else {
			set(left.getOffset(), LOCAL_0, section);
			add(left.getBase(), LOCAL_0, LOCAL_0, section);
			if(left.isRef()) {
				load(LOCAL_0, LOCAL_0, section);
			}
			load(LOCAL_0, LOCAL_0, section);
		}
		if(o.isAndOp()) {
			// compare expr1 with 0 (false)
			cmp(LOCAL_0, GLOBAL_0, section);
			be(".END_AND_" + getAndCount(), section);
		}
		else {	// OrOp
			cmp(LOCAL_0, String.valueOf(1), section);
			be(".END_OR_" + getOrCount(), section);
		}
	}
	
	public void printShortCircuitRight(STO right, Operator o, boolean isGlobal, String base, String offset) {
		StringBuffer sb = funcSB;
		String section = "func";
		makeComments("In short-circuit right", section);
		if(!right.hasAddress()) {
			sb.append(String.format(TWO_PARAM, SET_OP, ((ConstSTO)right).getIntValue(), LOCAL_0));
		}
		else {
			sb.append(String.format(TWO_PARAM, SET_OP, right.getOffset(), LOCAL_0));
			sb.append(String.format(THREE_PARAM, ADD_OP, right.getBase(), LOCAL_0, LOCAL_0));
			if(right.isRef()) {
				load(LOCAL_0, LOCAL_0, section);
			}
			load(LOCAL_0, LOCAL_0, section);
		}
		if(o.isAndOp()) {
			// compare expr2 with 0 (false)
			sb.append(String.format(TWO_PARAM, CMP_OP, LOCAL_0, GLOBAL_0));
			sb.append(String.format(BE_OP, ".END_AND_" + getAndCount()));
			sb.append(String.format(TWO_PARAM, MOVE_OP, 1, LOCAL_0));
			sb.append(String.format(BA_OP, ".DONE_AND_" + getAndCount()));
			sb.append(makeLabel("END_AND_" + getAndCount(), "", false));
			sb.append(String.format(TWO_PARAM, MOVE_OP, 0, LOCAL_0));
			sb.append(makeLabel("DONE_AND_"+String.valueOf(getAndCount()), "", false));
			setAndCount(getAndCount() + 1);
		}
		else {	// OrOp
			// expr1 is false, check expr2 against 1 (true)
			sb.append(String.format(TWO_PARAM, CMP_OP, LOCAL_0, String.valueOf(1)));
			sb.append(String.format(BE_OP, ".END_OR_" + getOrCount()));
			sb.append(String.format(TWO_PARAM, MOVE_OP, 0, LOCAL_0));
			sb.append(String.format(BA_OP, ".DONE_OR_" + getOrCount()));
			sb.append(makeLabel("END_OR_" + getOrCount(), "", false));
			sb.append(String.format(TWO_PARAM, MOVE_OP, 1, LOCAL_0));
			sb.append(makeLabel("DONE_OR_"+String.valueOf(getOrCount()), "", false));
			setOrCount(getOrCount() + 1);
		}
		sb.append(String.format(TWO_PARAM, SET_OP, offset, LOCAL_1));
		sb.append(String.format(THREE_PARAM, ADD_OP, base, LOCAL_1, LOCAL_1));
		sb.append(String.format(STORE_OP, LOCAL_0, LOCAL_1));
	}
	
	public void printIfStatement(STO expr, String funcName) {
		StringBuffer tmpBuffer = new StringBuffer();
		makeComments("if(" + expr.getName() + ")", "func");
		if(!expr.hasAddress()) {
			funcSB.append(String.format(TWO_PARAM, SET_OP, ((ConstSTO)expr).getIntValue(),LOCAL_0));
		}
		else {
			funcSB.append(String.format(TWO_PARAM, SET_OP, expr.getOffset(), LOCAL_0));
			funcSB.append(String.format(THREE_PARAM, ADD_OP, expr.getBase(), LOCAL_0, LOCAL_0));
			load(LOCAL_0, LOCAL_0, "func");
		}

		ifStmtLabelStack.push(tmpBuffer.append(".if." + String.valueOf(getIfStmtCount())));
		String label = ifStmtLabelStack.peek().toString();
		funcSB.append(String.format(TWO_PARAM, CMP_OP, LOCAL_0, GLOBAL_0));
		funcSB.append(String.format(BE_OP, label + ".else")); // peek + "else"
		setIfStmtCount(getIfStmtCount() + 1);
	}
	
	public void popIfStmtLabel() {
		if(!ifStmtLabelStack.empty()) {
			String label = ifStmtLabelStack.peek().toString();	// peek + "end"
			funcSB.append(label.toString() + ":" + NL);
			funcSB.append(String.format(BA_OP, label + ".end")); // peek + "else"
			funcSB.append(label + ".else" + ":" + NL); // label .OPTELSE[COUNT]:
		}
	}
	
	public void printElse() {
		// peek + "end"
		String label = ifStmtLabelStack.peek().toString();
		funcSB.append(label + ".end:" + NL);
		ifStmtLabelStack.pop();
	}
	//-------------------------------------------------------------------------------------------
	// printReturnStmt(FuncSTO funcSTO, STO expr)
	// - Added in Phase I.3
	// - Only called when there is an expr in return statement from the rule
	//   T_RETURN expr T_SEMI
	// - Do a ret/restore right away once we have gotten the value from the ConstSTO/ExprSTO
	//   to return from the function
	//-------------------------------------------------------------------------------------------
	public void printReturnStmt(FuncSTO funcSTO, STO expr) {
		if(funcSTO.isReturnByRef()) {
			makeComments(" Return by ref ", "funcSB");
			set(expr.getOffset(), LOCAL_0, "func");
			add(expr.getBase(), LOCAL_0, LOCAL_0, "func");
			if(expr.isRef()) {
				load(LOCAL_0, LOCAL_0, "func");
			}
			move(LOCAL_0, IN_1, "func");
		}
		else {
			if(!expr.hasAddress()) {
				if(expr.getType().isInt()) {
					if(funcSTO.getReturnType().isInt()) {
						set(((ConstSTO)expr).getIntValue(), IN_1, "func");
					}
					else {	// return type is float and expr is int 
						String label = floatNameGenerator();
						rodataSB.append(String.format(ALIGN, 4));
						rodataSB.append(makeLabel(label, "", false));
						rodataSB.append(String.format(SINGLE, "0r" + ((ConstSTO)expr).getFloatValue()));
						funcSB.append(String.format(TWO_PARAM, SET_OP, "."+label, LOCAL_1));
						load(LOCAL_1, FLOAT_0, "func");
					}
				}
				else if(expr.getType().isFloat()) {
					String label = floatNameGenerator();
					rodataSB.append(String.format(ALIGN, 4));
					rodataSB.append(makeLabel(label, "", false));
					rodataSB.append(String.format(SINGLE, "0r" + ((ConstSTO)expr).getFloatValue()));
					funcSB.append(String.format(TWO_PARAM, SET_OP, "."+label, LOCAL_1));
					load(LOCAL_1, FLOAT_0, "func");
				}
				else if(expr.getType().isBool()) {
					funcSB.append(String.format(TWO_PARAM, SET_OP, ((ConstSTO)expr).getIntValue(), IN_1));
				}
			}
			else { // expr is an ExprSTO/VarSTO
				set(expr.getOffset(), LOCAL_0, "func");
				add(expr.getBase(), LOCAL_0, LOCAL_0, "func");
				if(expr.isRef()) {
					load(LOCAL_0, LOCAL_0, "func");
				}
				if (expr.getType().isInt()) {
					if(funcSTO.getReturnType().isInt()) {
						load(LOCAL_0, IN_1, "func");
					}
					else {	// return type is float
						load(LOCAL_0, FLOAT_0, "func");
						funcSB.append(String.format(TWO_PARAM, FITOS_OP, FLOAT_0, FLOAT_0));
					}
				} 
				else if (expr.getType().isFloat()) { // float type
					load(LOCAL_0, FLOAT_0, "func");
				} 
				else if (expr.getType().isBool()){
					load(LOCAL_0, IN_1, "func");
				}
				else if (expr.getType().isPointerType()) {
					load(LOCAL_0, IN_1, "func");
				}
			}
		}
		funcSB.append(RETURN);
		funcSB.append(RESTORE);
	}
	
	public void printExitStmt(STO sto) {
		if(sto.isConst()) {
			funcSB.append(String.format(TWO_PARAM, SET_OP, ((ConstSTO)sto).getIntValue(), OUT_0));
			funcSB.append(String.format(CALL_OP, "exit"));
		}
		else {
			funcSB.append(String.format(TWO_PARAM, SET_OP, sto.getOffset(), LOCAL_0));
			funcSB.append(String.format(THREE_PARAM, ADD_OP, sto.getBase(), LOCAL_0, LOCAL_0));
			load(LOCAL_0, OUT_0, "func");
		}
	}
	//-------------------------------------------------------------------------------------------
	// printIncDec (Operator o, STO sto, String base, String offset, boolean prefix)
	//-------------------------------------------------------------------------------------------
	public void printIncDec(Operator o, STO sto, String base, String offset, boolean prefix, boolean isGlobal) {
		if(sto.getType().isFloat()) {
			printIncDecFloat(o, sto, base, offset, prefix, isGlobal);
			return;
		}
		StringBuffer sb;
		String section;
		if(isGlobal) {
			sb = globalSB;
			section = "global";
		}
		else {
			sb = funcSB;
			section = "func";
		}
		if(o.isIncOp() || o.isDecOp()) {
			// inc/dec result stored in %l1, address of sto is in %l0
			sb.append(String.format(TWO_PARAM, SET_OP, sto.getOffset(), LOCAL_0));
			sb.append(String.format(THREE_PARAM, ADD_OP, sto.getBase(), LOCAL_0, LOCAL_0));
			load(LOCAL_0, LOCAL_1, section);
			if(sto.isVar()) {
				if(((VarSTO)sto).isRef()) {
					load(LOCAL_1, LOCAL_1, section);
				}
			}
			if(sto.isExpr() && sto.isModLValue()) {
				load(LOCAL_1, LOCAL_1, section);
			}
			// %l0 -> ADDRESS OF VARSTO 
			// %l1 -> VALUE OF WHAT'S STORED IN VARSTO
			// %l2 -> RESULT OF INCREMENT/DECREMENT
			if(prefix) {
				makeComments(" " +o.getOpName() + sto.getName() + " ", section);
			}
			else {
				makeComments(" " +sto.getName() + o.getOpName() + " ", section);
			}
			int t = 1; // 1: primary type; 4: pointer type 
			if (sto.getType().isPointerType()){
				if(((PointerType)sto.getType()).getBaseType() != null) {
					t = ((PointerType)sto.getType()).getBaseType().getSize();
				}
			}
			if(o.isIncOp()) {
				sb.append(String.format(THREE_PARAM, ADD_OP, LOCAL_1, t, LOCAL_2));
			}
			else {	// DecOp
				sb.append(String.format(THREE_PARAM, SUB_OP, LOCAL_1, t, LOCAL_2));
			}
			sb.append(String.format(TWO_PARAM, SET_OP, offset, LOCAL_0));
			sb.append(String.format(THREE_PARAM, ADD_OP, base, LOCAL_0, LOCAL_0));
			if(prefix) {
				// store result to ExprSTO
				sb.append(String.format(STORE_OP, LOCAL_2, LOCAL_0));
			}
			else {
				// store original value of VarSTO into the ExprSTO
				sb.append(String.format(STORE_OP, LOCAL_1, LOCAL_0));
			}
			// store the incremented/decremented result back into VarSTO
			makeComments("storing the incremented/decremented result back into VarSTO", section);
			sb.append(String.format(TWO_PARAM, SET_OP, sto.getOffset(), LOCAL_0));
			sb.append(String.format(THREE_PARAM, ADD_OP, sto.getBase(), LOCAL_0, LOCAL_0));
			if(sto.isRef()) {
				load(LOCAL_0, LOCAL_0, section);
			}
			sb.append(String.format(STORE_OP, LOCAL_2, LOCAL_0) + NL);
		}
	}
	
	public void printIncDecFloat(Operator o, STO sto, String base, String offset, boolean prefix, boolean isGlobal) {
		StringBuffer sb;
		String section;
		if(isGlobal) {
			sb = globalSB;
			section = "global";
		}
		else {
			sb = funcSB;
			section = "func";
		}
		// inc/dec result stored in %l1, address of sto is in %l0
		sb.append(String.format(TWO_PARAM, SET_OP, sto.getOffset(), LOCAL_0));
		sb.append(String.format(THREE_PARAM, ADD_OP, sto.getBase(), LOCAL_0, LOCAL_0));
		if(sto.isRef()) {
			load(LOCAL_0, LOCAL_0, section);
		}
		load(LOCAL_0, FLOAT_1, section);
		sb.append(String.format(TWO_PARAM, SET_OP, ".float_one", LOCAL_0));
		load(LOCAL_0, FLOAT_2, section);
		// %l0 -> ADDRESS OF VARSTO 
		// %f1 -> VALUE OF WHAT'S STORED IN VARSTO
		// %f2 -> RESULT OF INCREMENT/DECREMENT
		if(prefix) {
			makeComments(o.getOpName() + sto.getName(), section);
		}
		else {
			makeComments(sto.getName() + o.getOpName(), section);
		}
		if(o.isIncOp()) {
			sb.append(String.format(THREE_PARAM, FADDS_OP, FLOAT_1, FLOAT_2, FLOAT_2));
		}
		else {	// DecOp
			sb.append(String.format(THREE_PARAM, FSUBS_OP, FLOAT_1, FLOAT_2, FLOAT_2));
		}
		sb.append(String.format(TWO_PARAM, SET_OP, offset, LOCAL_0));
		sb.append(String.format(THREE_PARAM, ADD_OP, base, LOCAL_0, LOCAL_0));
		if(prefix) {
			// store result to ExprSTO
			sb.append(String.format(STORE_OP, FLOAT_2, LOCAL_0));
		}
		else {
			// store original value of VarSTO into the ExprSTO
			sb.append(String.format(STORE_OP, FLOAT_1, LOCAL_0));
		}
		// store the incremented/decremented result back into VarSTO
		makeComments("storing the incremented/decremented result back into VarSTO", section);
		sb.append(String.format(TWO_PARAM, SET_OP, sto.getOffset(), LOCAL_0));
		sb.append(String.format(THREE_PARAM, ADD_OP, sto.getBase(), LOCAL_0, LOCAL_0));
		if(sto.isRef()) {
			load(LOCAL_0, LOCAL_0, section);
		}
		sb.append(String.format(STORE_OP, FLOAT_2, LOCAL_0) + NL);
	}
	
	public void printUnaryExpr(STO sto, Operator o, String base, String offset, boolean isGlobal) {
		StringBuffer sb;
		String section;
		if(isGlobal) {
			sb = globalSB;
			section = "global";
		}
		else {
			sb = funcSB;
			section = "func";
		}
		if(o.isNotOp()) {
			if(sto.hasAddress()) {
				sb.append(String.format(TWO_PARAM, SET_OP, sto.getOffset(), LOCAL_0));
				sb.append(String.format(THREE_PARAM, ADD_OP, sto.getBase(), LOCAL_0, LOCAL_0));
				load(LOCAL_0, LOCAL_0, section);
			}
			else {
				sb.append(String.format(TWO_PARAM, SET_OP, ((ConstSTO)sto).getIntValue(), LOCAL_0));
			}
			sb.append(String.format(THREE_PARAM, XOR_OP, LOCAL_0, 1, LOCAL_0));
		}
		sb.append(String.format(TWO_PARAM, SET_OP, offset, LOCAL_1));
		sb.append(String.format(THREE_PARAM, ADD_OP, base, LOCAL_1, LOCAL_1));
		sb.append(String.format(STORE_OP, LOCAL_0, LOCAL_1) + NL);
	}
	
	/****************************** PHASE IIA: ASSIGNMENT STATEMENT **************************/
	public void printAssign(STO left, STO right, STO expr, boolean isGlobal) {
		StringBuffer sb;
		String section;
		if (isGlobal){
			sb = globalSB;
			section = "global";
		} 
		else {
			sb = funcSB;
			section = "func";
		}
		makeComments("Do Assignment: " + left.getName() + " = " + right.getName(), section);
		if (left.getType().isFloat()) { // 
			printAssignFloat(left,right, expr, isGlobal);
			return;
		} 
		else if (left.getType().isPointerType()){
			set(left.getOffset(), LOCAL_0, section);
			add(left.getBase(), LOCAL_0, LOCAL_0, section);
			if(left.isRef()) { // maybe array of pointer, after dereferencing becomes ptrType
				load(LOCAL_0, LOCAL_0, section);
			}
			if(right.hasAddress()) {
				set(right.getOffset(), LOCAL_1, section);
				add(right.getBase(), LOCAL_1, LOCAL_1, section);
				System.out.println("do assign");
				System.out.println(right.getName() + "ref: " + right.isRef() + " addressof: " + right.isAddressOfTypeCast());
				if(right.getType().isPointerType() && !right.isAddressOfTypeCast()) {
					load(LOCAL_1, LOCAL_1, section);
				}
				if(right.isRef() || right.isAddressOfTypeCast()) {
					load(LOCAL_1, LOCAL_1, section);
				}
			}
			else {	// nullptr
				set(0, LOCAL_1, section);
			}
			store(LOCAL_1, LOCAL_0, section);
		}
		else if (left.getType().isStructType()) {
			makeComments("Call memcpy for left and right", section);
			set(left.getOffset(), LOCAL_0, section);
			add(left.getBase(), LOCAL_0, LOCAL_0,section);
			if(left.isRef()) {
				load(LOCAL_0, OUT_0, section);
			}
			else {
				move(LOCAL_0, OUT_0, section);
			}
			set(right.getOffset(),LOCAL_0,section);
			add(right.getBase(),LOCAL_0,LOCAL_0,section);
			if(right.isRef()) {
				load(LOCAL_0, OUT_1, section);
			}
			else {
				move(LOCAL_0, OUT_1, section);
			}
			set(right.getType().getSize(), OUT_2, section);
			call("memcpy", section);
			makeComments("Call memcpy for expr and right", section);
			set(expr.getOffset(), LOCAL_0, section);
			add(expr.getBase(), LOCAL_0, LOCAL_0, section);
			move(LOCAL_0, OUT_0, section);
			set(right.getOffset(), LOCAL_0, section);
			add(right.getBase(),LOCAL_0, LOCAL_0, section);
			if(right.isRef()) {
				load(LOCAL_0, OUT_1, section);
			}
			else {
				move(LOCAL_0, OUT_1, section);
			}
			set(right.getType().getSize(), OUT_2, section);
			call("memcpy",section);
		}
		else { // if both left and right are not Float
			if (!right.hasAddress()) {
				makeComments(" RHS : " + right.getName(), section);
				sb.append(String.format(TWO_PARAM, SET_OP, ((ConstSTO)right).getIntValue(), LOCAL_1));
			}
			else {
				makeComments("Load RHS value: " + right.getName(), section);
				sb.append(String.format(TWO_PARAM, SET_OP, right.getOffset(), LOCAL_0));
				sb.append(String.format(THREE_PARAM, ADD_OP, right.getBase(), LOCAL_0, LOCAL_0));
				if (right.isRef()){
					load(LOCAL_0, LOCAL_0, section);
				}
				load(LOCAL_0, LOCAL_1, section);
			}
			makeComments("Load LHS location: " + left.getName(), section);
			sb.append(String.format(TWO_PARAM, SET_OP, left.getOffset(), LOCAL_0));
			sb.append(String.format(THREE_PARAM, ADD_OP, left.getBase(), LOCAL_0, LOCAL_0));
			if(left.isRef()) {
				load(LOCAL_0, LOCAL_0, section);
			}
			makeComments("Store RHS Value to LHS location ", section);
			sb.append(String.format(STORE_OP, LOCAL_1, LOCAL_0));
			
			makeComments(" Store the result of RHS to expr", section);
			sb.append(String.format(TWO_PARAM, SET_OP, expr.getOffset(), LOCAL_0));
			sb.append(String.format(THREE_PARAM, ADD_OP, expr.getBase(), LOCAL_0, LOCAL_0));
			sb.append(String.format(STORE_OP, LOCAL_1, LOCAL_0));
		}
	}
	
	void printAssignFloat(STO left, STO right, STO expr, boolean isGlobal){
		StringBuffer sb;
		String section;
		if (isGlobal){
			sb = globalSB;
			section = "global";
		} 
		else {
			sb = funcSB;
			section = "func";
		}
		if (!right.hasAddress()){
			ConstSTO rValue= (ConstSTO) right;
			String label = floatNameGenerator();
			rodataSB.append(String.format(ALIGN, 4));
			rodataSB.append(makeLabel(label, "", false));
			rodataSB.append(String.format(SINGLE, "0r" + rValue.getFloatValue()));
			sb.append(String.format(TWO_PARAM, SET_OP, "."+label, LOCAL_1));
			load(LOCAL_1, FLOAT_1, section);
		} 
		else { // right has base and offset
			makeComments("Load RHS value: " + right.getName(), section);
			sb.append(String.format(TWO_PARAM, SET_OP, right.getOffset(), LOCAL_0));
			sb.append(String.format(THREE_PARAM, ADD_OP, right.getBase(), LOCAL_0, LOCAL_0));
			if(right.isRef()) {
				load(LOCAL_0, LOCAL_0, section);
			}
			load(LOCAL_0, FLOAT_1, section);
			if ( right.getType().isInt() ){
				sb.append(String.format(TWO_PARAM, FITOS_OP, FLOAT_1, FLOAT_1)); // change bit pattern				
			}	
		}
		// LHS
		makeComments("Load LHS location: " + left.getName(), section);
		sb.append(String.format(TWO_PARAM, SET_OP, left.getOffset(), LOCAL_0));
		sb.append(String.format(THREE_PARAM, ADD_OP, left.getBase(), LOCAL_0, LOCAL_0));
		if(left.isRef()) {
			load(LOCAL_0, LOCAL_0, section);
		}
		makeComments("Store RHS Value to LHS location ", section);
		sb.append(String.format(STORE_OP, FLOAT_1, LOCAL_0));
		
		makeComments(" Store the result of RHS to expr", section);
		sb.append(String.format(TWO_PARAM, SET_OP, expr.getOffset(), LOCAL_0));
		sb.append(String.format(THREE_PARAM, ADD_OP, expr.getBase(), LOCAL_0, LOCAL_0));
		sb.append(String.format(STORE_OP, FLOAT_1, LOCAL_0));
	}
	
	
	/**************************PHASE IIA: CIN ****************************/
	void printCin(STO sto) {
		makeComments("Input value to variable : " + sto.getName(), "func");
		funcSB.append(String.format(TWO_PARAM, SET_OP, sto.getOffset(), LOCAL_0));
		funcSB.append(String.format(THREE_PARAM, ADD_OP, sto.getBase(), LOCAL_0, LOCAL_0));
		
		if (sto.getType().isInt()) {
			funcSB.append(INPUT_INT);
			funcSB.append(String.format(STORE_OP, OUT_0, LOCAL_0));
		} 
		else {
			funcSB.append(INPUT_FLOAT);
			funcSB.append(String.format(STORE_OP, FLOAT_0, LOCAL_0));
		}
	}
	
	/**************************PHASE IIB: WHILE/BREAK/CONTINUE ****************************/
	public void printWhile1() {
		loopLabelStack.push("loop" + getLoopCount());
		setLoopCount(getLoopCount() + 1);
		String label = loopLabelStack.peek();
		funcSB.append(makeLabel(label+"start", "", false));
	}
	public void printWhile2(STO expr) {
		String label = loopLabelStack.peek();
		if(expr.hasAddress()) {	// addressable -> get offset and base
			funcSB.append(String.format(TWO_PARAM, SET_OP, expr.getOffset(), LOCAL_0));
			funcSB.append(String.format(THREE_PARAM, ADD_OP, expr.getBase(), LOCAL_0, LOCAL_0));
			load(LOCAL_0, LOCAL_0, "func");
		}
		else { // constant literal 
			funcSB.append(String.format(TWO_PARAM, SET_OP, ((ConstSTO)expr).getIntValue(), LOCAL_0));
		}
		
		funcSB.append(String.format(TWO_PARAM, CMP_OP, LOCAL_0, GLOBAL_0));
		funcSB.append(String.format(BE_OP, "." + label + "end"));
	}
	public void printWhile3() {
		String label = loopLabelStack.peek();
		funcSB.append(String.format(BA_OP, "." + label + "start")); // ba .L1start
		funcSB.append(makeLabel(label+"end", "", false)); // .L1end:
		loopLabelStack.pop();
	}
	
	public void printBreak() {
		String label = loopLabelStack.peek();
		ba("." + label + "end", "func");
	}
	
	public void printContinue() {
		String label = loopLabelStack.peek();
		if(label.contains("foreach")) {
			ba("." + label + "incr", "func");
		}
		else {
			ba("." + label + "start", "func");
		}
	}
	
	/***************************PHASE IIC: ARRAY *****************************************/
	public void printArrayIndexing(STO des, STO index, STO resultExpr, boolean isGlobal) {
		String section;
		if(isGlobal) {
			section = "global";
		}
		else {
			section = "func";
		}
		if(des.getType().isArrayType()) {
			makeComments("Indexing array: " + des.getName(), section);
			set(des.getOffset(), LOCAL_0, section);
			add(des.getBase(), LOCAL_0, LOCAL_0, section);
			if(des.isRef()) {
				load(LOCAL_0, LOCAL_0, section);
			}
			if(index.hasAddress()) {
				set(index.getOffset(), LOCAL_1, section);
				add(index.getBase(), LOCAL_1, LOCAL_1, section);
				if(index.isRef()) {
					load(LOCAL_1, LOCAL_1, section);
				}
				load(LOCAL_1, OUT_0, section);
			}
			else {	// ConstSTO
				set(String.valueOf(((ConstSTO)index).getIntValue()), OUT_0, section);
			}
			Type baseType = resultExpr.getType();	// base type of the array
			set(String.valueOf(baseType.getSize()), OUT_1, section);
			call(".mul", section);
			add(LOCAL_0, OUT_0, LOCAL_0, section);	// %l0 has the offset of arr[index]
			// store back onto the stack (ExprSTO)
			set(resultExpr.getOffset(), LOCAL_1, section);
			add(resultExpr.getBase(), LOCAL_1, LOCAL_1, section);	// %l1 has the address of the exprSTO
			store(LOCAL_0, LOCAL_1, section);
		}
		else {	// PointerType
			set(des.getOffset(), LOCAL_0, section);
			add(des.getBase(), LOCAL_0, LOCAL_0, section);
			if(des.isRef()) {
				load(LOCAL_0, LOCAL_0, section);
			}
			load(LOCAL_0, LOCAL_0, section);
			if(index.hasAddress()) {
				set(index.getOffset(), LOCAL_1, section);
				add(index.getBase(), LOCAL_1, LOCAL_1, section);
				if(index.isRef()) {
					load(LOCAL_1, LOCAL_1, section);
				}
				load(LOCAL_1, OUT_0, section);
			}
			else {	// ConstSTO
				set(String.valueOf(((ConstSTO)index).getIntValue()), OUT_0, section);
			}
			Type baseType = resultExpr.getType();	// base type of the array
			set(String.valueOf(baseType.getSize()), OUT_1, section);
			call(".mul", section);
			add(LOCAL_0, OUT_0, LOCAL_0, section);	// %l0 has the offset of p+offset
			// store back onto the stack (ExprSTO)
			set(resultExpr.getOffset(), LOCAL_1, section);
			add(resultExpr.getBase(), LOCAL_1, LOCAL_1, section);	// %l1 has the address of the exprSTO
			store(LOCAL_0, LOCAL_1, section);
		}
	}
	
	public void printForeach(STO iterVar, STO iterable) {
		/* initialize loop counter (iterVar) to 0, store the variable on stack */
		loopLabelStack.push("foreach" + getLoopCount());
		setLoopCount(getLoopCount() + 1);
		set(iterVar.getOffset(), LOCAL_0, "func");
		add(iterVar.getBase(), LOCAL_0, LOCAL_0, "func");
		set(String.valueOf(0), LOCAL_1, "func");
		store(LOCAL_1, LOCAL_0, "func");
		
		String label = loopLabelStack.peek();
		label(label + "start", "", false, "func");
		/* compare loop counter to numElements of Array */
		set(iterVar.getOffset(), LOCAL_0, "func");
		add(iterVar.getBase(), LOCAL_0, LOCAL_0, "func");
		load(LOCAL_0, LOCAL_0, "func");
		int numOfElements = ((ArrayType)iterable.getType()).getNumOfElements();
		set(String.valueOf(numOfElements), LOCAL_1, "func");
		cmp(LOCAL_0, LOCAL_1, "func");
		bge("." + label + "end", "func");
		
		/* populate iteration variable memory space */
		makeComments("Populating iteration variable memory space: " + iterable.getName(), "func");
		String currIterVarOffset = "-" + String.valueOf(MyParser.getLocalVarSize());
		set(currIterVarOffset, LOCAL_0, "func");
		add(FP, LOCAL_0, LOCAL_0, "func");
		
		makeComments("Indexing array: " + iterable.getName(), "func");
		set(iterVar.getOffset(), LOCAL_1, "func");
		add(iterVar.getBase(), LOCAL_1, LOCAL_1, "func");
		load(LOCAL_1, OUT_0, "func");
			
		Type baseType = ((ArrayType)iterable.getType()).getBaseType();	// base type of the array
		set(String.valueOf(baseType.getSize()), OUT_1, "func");
		call(".mul", "func");
		add(LOCAL_0, OUT_0, LOCAL_1, "func");	// %l0 has the offset of arr[index]
		
		// store back onto the stack (ExprSTO)
		store(LOCAL_1, LOCAL_0, "func");
	}
	
	public void incForEach(STO iterVar) {
		String label = loopLabelStack.peek();
		/* increment loop counter by 1 */
		label(label + "incr", "", false, "func");
		set(iterVar.getOffset(), LOCAL_0, "func");
		add(iterVar.getBase(), LOCAL_0, LOCAL_0, "func");
		load(LOCAL_0, LOCAL_1, "func");
		add(String.valueOf(1), LOCAL_1, LOCAL_1, "func");
		store(LOCAL_1, LOCAL_0, "func");
		
		/* loop back (branch always to start) */
		ba("." + label + "start", "func");
	}
	public void popForEachLabel() {
		String label = loopLabelStack.peek();
		label(label + "end", "", false, "func");
		loopLabelStack.pop();
	}
	
	/************************ PHASE IIC: STRUCTS ******************************/
	public void structAccess(STO structVar, STO field, STO expr) {
		makeComments("Accessing Struct field: " + field.getName(), "func");
		if(!field.getType().isFuncPtrType()) {
			set(structVar.getOffset(), LOCAL_0, "func");
			add(structVar.getBase(), LOCAL_0, LOCAL_0, "func");	// %l0 -> struct's location
			if(structVar.isRef()) {
				load(LOCAL_0, LOCAL_0, "func");
			}
			set(field.getOffset(), LOCAL_1, "func"); // %l1 -> field's offset from struct
			
			/* store the struct.field address into expr */
			if(field.isRef()) {
				load(LOCAL_1, LOCAL_1, "func");
			}
			add(LOCAL_0, LOCAL_1, LOCAL_0, "func"); // %l0 -> address of struct.field
			set(expr.getOffset(), LOCAL_1, "func"); 
			add(expr.getBase(), LOCAL_1, LOCAL_1, "func"); // %l1 -> address of exprSTO allocated on the stack in MyParser
			store(LOCAL_0, LOCAL_1, "func");
		}
		else {
			set(structVar.getOffset(), LOCAL_0, "func");
			add(structVar.getBase(), LOCAL_0, LOCAL_0, "func");
			if(structVar.isRef()) {	// this.func() need to load the struct address stored inside %fp+68
				load(LOCAL_0, LOCAL_0, "func");
			}
			add(SP, String.valueOf(68), LOCAL_1, "func");
			store(LOCAL_0, LOCAL_1, "func");
		}
	}
	
	/************************ PHASE IIIA: POINTERS ********************************/
	public void printDereference(STO des, STO expr, boolean isGlobal) {
		String section;
		if(isGlobal) {
			section = "global";
		}
		else {
			section = "func";
		}
		makeComments("Dereferencing: " + des.getName(), section);
		set(des.getOffset(), LOCAL_0, section);
		add(des.getBase(), LOCAL_0, LOCAL_0, section);
		if(des.isRef() && !des.isAddressOfTypeCast()) {	// corner case: *&
			load(LOCAL_0, LOCAL_0, section);
		}
		load(LOCAL_0, LOCAL_0, section);
		
		/* load address of exprSTO and store the dereferenced ptr onto the stack */
		set(expr.getOffset(), LOCAL_1, section);
		add(expr.getBase(), LOCAL_1, LOCAL_1, section);
		store(LOCAL_0, LOCAL_1, section);
	}
	
	public void printNew(STO des){
		makeComments("New (Call Calloc)", "func");
		set("1", OUT_0,"func");		
		set(((PointerType)des.getType()).getBaseType().getSize(), OUT_1,"func");// or size of basetype of the pointer
		call("calloc","func");
		set(des.getOffset(), LOCAL_0, "func");
		add(des.getBase(),LOCAL_0,LOCAL_0,"func");
		if(des.isRef()) {
			makeComments("Store address of pointer to heap in ExprSTO", "func");
			load(LOCAL_0, LOCAL_0, "func");
		}
		store(OUT_0,LOCAL_0,"func");		
	}
	
	public void printDelete(STO des){
		makeComments("Delete (Call Free)", "func");
		set(des.getOffset(), LOCAL_0, "func");
		add(des.getBase(), LOCAL_0, LOCAL_0, "func");
		load(LOCAL_0, OUT_0,"func");
		call("free", "func");
		set(des.getOffset(), LOCAL_0, "func");
		add(des.getBase(), LOCAL_0, LOCAL_0, "func");
		store(GLOBAL_0, LOCAL_0, "func");
	}

	public void printAddress(STO des, STO result, boolean isGlobal) {
		makeComments(" Address of " + des.getName() ,"func");
		String section;
		if (isGlobal){
			section = "global";
		}
		else {
			section = "func";	
		}
		set(des.getOffset(),LOCAL_0,section);
		add(des.getBase(),LOCAL_0,LOCAL_0,section);
		if(des.isRef()) {
			makeComments(des.getName() + " is a ref ","func");
			load(LOCAL_0, LOCAL_0, section);
		}
		set(result.getOffset(),LOCAL_1,section);
		add(result.getBase(),LOCAL_1,LOCAL_1,section);
		
		store(LOCAL_0, LOCAL_1, section);
	}
}